package org.myioc.util;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.myioc.entity.Bean;
import org.myioc.entity.property;

public class ClassPathXmlApplicationContext implements BeanFactory {

	// 我用的IOC用HashMap实现
	private Map<String, Object> ioc = new HashMap<String, Object>();
	// 这个引用是用来接配置文件返回的list的
	private List<Bean> config;

	// 不让外界调用无参构造器，把它用private修饰一下
	private ClassPathXmlApplicationContext() {
	}

	// 带配置文件路径的构造器
	public ClassPathXmlApplicationContext(String path) {
		// 调用XMLconfig得到XML配置文件对应的list对象
		config = XMLconfig.readXML(path);

		if (config != null) {
			// 遍历里面的bean
			for (Bean bean : config) {
				createBean(bean);
			}
		}

	}

	public void createBean(Bean bean) {
		//设置标志提示进入递归
		int flag = 0;
		if (ioc.containsKey(bean.getId())) {
			return;
		}
		// 通过bean得到id
		String id = bean.getId();
		// 通过bean得到ClassName
		String ClassName = bean.getClassName();
		// 通过bean得到propertyList集合
		List<property> list = bean.getList();

		Class clazz = null;
		try {
			// 得到ClassName名字的Class对象
			clazz = Class.forName(ClassName);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new RuntimeException("类的路径写错了");
		}

		Object object = null;
		try {
			// 利用反射调用这个对象的无参构造器
			object = clazz.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
			throw new RuntimeException("该类没有无参构造方法");
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new RuntimeException("该类没有无参构造方法");
		}

		if (list != null) {
			// 遍历propertyList集合
			for (property p : list) {
				// 情况一：配置文件里用Value注入,同时判断一下容器里有没有这个对象了，有就不用再注入了
				if (p.getValue() != null && !ioc.containsKey(bean.getId())) {
					setProperty(object, p, p.getValue());
				}
				// 情况二:配置文件里用ref注入,同时判断一下容器里有没有这个对象了，有就不用再注入了
				if (p.getRef() != null && !ioc.containsKey(bean.getId())) {
					// 在 IOC里找这个依赖的对象有没有被注入过
					Object obj = ioc.get(p.getRef());
					//循环判断,这里用while是因为会有第三种情况，就是通过循环之后找
					//到依赖对象了，所以有flag == 1 && obj == null这种情况
					//如果用if判断，就和下面的obj!=NULL互斥，那么第三种情况就不会有了
					while (obj == null) {
						flag = 0;
						for (int i = 0; i < config.size(); i++) {
							//这个字符串指的是Bean的id
							String temp = config.get(i).getId();
							//bean的id如果和property的ref的值一样的话说明集合里是有这个对象的，那么把这个
							//对象先放到IOC容器中就可以了
							if (temp.equals(p.getRef())) {
								//递归
								createBean(config.get(i));
								flag = 1;
								break;
							}
						}
						if(flag == 1){
							break;
						}else {
							throw new RuntimeException("没有依赖对象" + p.getRef());
						}
					}
					//依赖在IOC容器中，直接注入
					if(obj != null){
						setProperty(object, p, obj);
					}
					//通过循环找到依赖，注入
					if (flag == 1 && obj == null) {
						Object obj1 = ioc.get(p.getRef());
						setProperty(object, p, obj1);
					}
				}
			}
			// 把对象放到IOC容器中， key为bean的id，value为造出来的对象
			ioc.put(id, object);

		}
	}

	public void setProperty(Object object,property p,Object obj){
		try {
			// 如果有这个对象了，把这个对象注入到object里
			BeanUtils.setProperty(object, p.getName(), obj);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new RuntimeException("依赖对象的名写错了" + p.getRef());
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	public Object getBean(String BeanName) {
		return ioc.get(BeanName);
	}

}
